home *** CD-ROM | disk | FTP | other *** search
/ The Arsenal Files 6 / The Arsenal Files 6 (Arsenal Computer).ISO / prg_basi / ddfedit.zip / AUTOTABS.BAS < prev    next >
BASIC Source File  |  1996-02-02  |  11KB  |  351 lines

  1. Option Explicit
  2.  
  3. Declare Function nlWinSetTabStops Lib "User" Alias "SendMessage" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long
  4.  
  5. Declare Function nlWinAPI_GetTextExtent Lib "GDI" Alias "GetTextExtent" (ByVal hDC As Integer, ByVal lpString As String, ByVal nCount As Integer) As Long
  6. Declare Function nlWinAPI_GetDialogBaseUnits Lib "User" Alias "GetDialogBaseUnits" () As Long
  7.  
  8. Global Const nWM_USER = 1024
  9. Global Const nLB_SETTABSTOPS = nWM_USER + 19
  10. Global Const nEM_SETTABSTOPS = nWM_USER + 27
  11. Global Const nCB_SELECTSTRING = nWM_USER + 13
  12. Global Const nLB_SELECTSTRING = nWM_USER + 13
  13.  
  14. Global Const nSEARCH_FROM_TOP = -1
  15.  
  16. Function AutoSetTabStopsCheck (ListCtrl As Control, TextCtrl As Control, tfUseHeadingWidthsOnly As Integer, SetDefaultTabs As Integer) As Integer
  17.  
  18. 'This function automatically calculates and sets appropriate
  19. 'tabstops for a multi-column listbox, based on the actual data
  20. 'in the listbox.  You do not have to tell the function how many
  21. 'columns you want, nor figure out how wide each column should be;
  22. 'the actual data placed into the listbox determines that.
  23.  
  24. 'In addition to the listbox, the function also sets identical
  25. 'tabstops in an accompanying, multi-line textbox.  This textbox
  26. 'provides the data for the column headings.
  27.  
  28. 'tfUseHeadingWidthsOnly:
  29. '  True -  Tabstops are calculated based only on the
  30. '          widths of the column headings. This option
  31. '          is must faster, but you're gambling that the
  32. '          actual data will always be narrower than the
  33. '          headings.
  34. '
  35. '  False - Tabstops are calculated based on the widest
  36. '          entry in each column; both the headings and
  37. '          the data are examined.  This option is slower
  38. '          because each entry in the listbox must be
  39. '          parsed, but it eliminates the guesswork.
  40.  
  41. 'SetDefaultTabs:
  42. '  True -  Tabstops are reset to Windows' default intervals
  43. '          of 8 dialog units.
  44. '
  45. '  False - Tabstops are calculated based on the actual
  46. '          data in the listbox/textbox.
  47. '
  48. '
  49. 'The function itself returns FALSE if any of the control
  50. 'verification tests fail; otherwise it returns TRUE.
  51.  
  52.  
  53. Dim sColHeadings As String, sColData As String, sColString As String
  54. Dim sParentFontName As String, fParentFontSize As Single
  55. Dim tfParentFontBold As Integer, tfParentFontItalic As Integer
  56. Dim nColCount As Integer, nDataWidth As Integer, nSpaceBetweenCols As Integer
  57. Dim nMaxListboxCols As Integer, nNbrListboxCols As Integer, nNbrTabstops As Integer
  58. Dim nInStart As Integer, nTabPos As Integer
  59. Dim nlistsub As Integer, nTabSub As Integer
  60. Dim nlRC As Long
  61. Dim nListFontAvgWidth As Integer, nSystemFontAvgWidth As Integer
  62. Dim fListFontPixelsPerDlgUnit As Single, fFontRatio As Single
  63. Dim i As Integer
  64. Dim nColWidth() As Integer  'measured column widths
  65. Dim nTabstop() As Integer   'calculated WinAPI tabstops
  66.  
  67. Dim Msg As String
  68. '================
  69. Main:
  70. '================
  71.  
  72.  
  73. GoSub VerifyControls
  74. GoSub Initialize
  75.  
  76. If SetDefaultTabs Then
  77.    nNbrTabstops = 0
  78.    GoSub UpdateCtrls
  79. Else
  80.    'Since VB provides an hDC property for forms, but
  81.    'not for controls, we must temporarily set the parent
  82.    'form's font characteristics equal to the listbox's
  83.    'font characteristics.  Doing this ensures that all
  84.    'text measurements made using the form's DC will be
  85.    'accurate for the listbox.
  86.  
  87.    sParentFontName = ListCtrl.Parent.FontName
  88.    fParentFontSize = ListCtrl.Parent.FontSize
  89.    tfParentFontBold = ListCtrl.Parent.FontBold
  90.    tfParentFontItalic = ListCtrl.Parent.FontItalic
  91.    ListCtrl.Parent.FontName = ListCtrl.FontName
  92.    ListCtrl.Parent.FontSize = ListCtrl.FontSize
  93.    ListCtrl.Parent.FontBold = ListCtrl.FontBold
  94.    ListCtrl.Parent.FontItalic = ListCtrl.FontItalic
  95.  
  96.    'Identify and measure the width of the column headings
  97.    'present in the textbox.
  98.  
  99.    GoSub MeasureColHeadingWidths
  100.  
  101.    'Measure the width of the column data values present
  102.    'in the listbox.
  103.  
  104.    If Not tfUseHeadingWidthsOnly Then
  105.       GoSub MeasureColDataWidths
  106.    End If
  107.  
  108.    'Calculate and set the necessary tabstop values, based
  109.    'on the maximum width of each column.
  110.  
  111.    GoSub UpdateCtrls
  112.  
  113.    'Reset the parent form's font characteristics to their
  114.    'original values.
  115.  
  116.    ListCtrl.Parent.FontName = sParentFontName
  117.    ListCtrl.Parent.FontSize = fParentFontSize
  118.    ListCtrl.Parent.FontBold = tfParentFontBold
  119.    ListCtrl.Parent.FontItalic = tfParentFontItalic
  120. End If
  121.  
  122. Exit Function
  123.  
  124.  
  125. '==========================
  126. VerifyControls:
  127. '==========================
  128. 'Make sure both controls are of the proper type,
  129. 'and that the necessary property values are set.
  130.  
  131. If TypeOf ListCtrl Is ListBox Then
  132.   ' nothing
  133. Else
  134.    Exit Function
  135. End If
  136.  
  137. If TypeOf TextCtrl Is TextBox Then
  138.   ' nothing
  139. Else
  140.    Exit Function
  141. End If
  142.  
  143. If ListCtrl.Columns <> 0 Then
  144.    Exit Function
  145. End If
  146.  
  147. If TextCtrl.MultiLine = False Then
  148.    Exit Function
  149. End If
  150.  
  151. If TextCtrl.BorderStyle <> 0 Then
  152.    Exit Function
  153. End If
  154.  
  155. If Len(TextCtrl.Text) = 0 Then
  156.    Exit Function
  157. End If
  158.  
  159. Return
  160.        
  161. '======================
  162. Initialize:
  163. '======================
  164. 'A little extra space between columns helps
  165. 'to mitigate the inevitable rounding errors
  166. 'that will occur in the tabstop calculations.
  167.  
  168. nSpaceBetweenCols = 3
  169.  
  170. nMaxListboxCols = 10
  171. ReDim nColWidth(nMaxListboxCols)
  172.  
  173. Return
  174.  
  175. '===================================
  176. MeasureColHeadingWidths:
  177. '===================================
  178. 'Search for TAB characters in the column heading
  179. 'text.  For each column found, measure the width
  180. 'of the heading text.
  181.  
  182. sColHeadings = TextCtrl.Text
  183. nNbrListboxCols = 1
  184.  
  185. nInStart = 1
  186. Do
  187.    nTabPos = InStr(nInStart, sColHeadings, Chr$(9))
  188.  
  189.    If nTabPos > 0 Then
  190.       sColString = Mid$(sColHeadings, nInStart, nTabPos - nInStart)
  191.    Else
  192.       sColString = Mid$(sColHeadings, nInStart, Len(sColHeadings) - nInStart + 1)
  193.    End If
  194.  
  195.    'Measure the length of the string, in pixels;
  196.    'this value is the current "column width".
  197.    
  198.    sColString = sColString + Space$(nSpaceBetweenCols)
  199.    nColWidth(nNbrListboxCols) = nlWinAPI_GetTextExtent(ListCtrl.Parent.hDC, sColString, Len(sColString)) Mod 65536
  200.    
  201.  
  202.  
  203.    If nTabPos > 0 Then
  204.       nNbrListboxCols = nNbrListboxCols + 1
  205.  
  206.       'Allocate space for more columns, if necessary
  207.  
  208.       If nNbrListboxCols > nMaxListboxCols Then
  209.      nMaxListboxCols = nNbrListboxCols
  210.      ReDim Preserve nColWidth(nMaxListboxCols)
  211.       End If
  212.  
  213.       If nTabPos < Len(sColHeadings) Then
  214.      nInStart = nTabPos + 1
  215.       End If
  216.    End If
  217. Loop Until nTabPos = 0
  218.  
  219. nNbrTabstops = nNbrListboxCols - 1
  220.  
  221. Return
  222.  
  223. '================================
  224. MeasureColDataWidths:
  225. '================================
  226. 'Search for TAB characters in the listbox data.
  227. 'For each column found, measure the width of
  228. 'the data.
  229.  
  230. For nlistsub = 0 To ListCtrl.ListCount - 1
  231.   
  232.     If Len(ListCtrl.List(nlistsub)) > 0 Then
  233.       sColData = ListCtrl.List(nlistsub)
  234.       nColCount = 1
  235.  
  236.       nInStart = 1
  237.       Do
  238.       nTabPos = InStr(nInStart, sColData, Chr$(9))
  239.       'Debug.Print nTabPos
  240.       If nTabPos > 0 Then
  241.         sColString = Mid$(sColData, nInStart, nTabPos - nInStart)
  242.         'Debug.Print sColString
  243.       Else
  244.         sColString = Mid$(sColData, nInStart, Len(sColData) - nInStart + 1)
  245.       End If
  246.  
  247.       'Measure the length of the string, in pixels
  248.     
  249.       sColString = sColString + Space$(nSpaceBetweenCols)
  250.       nDataWidth = nlWinAPI_GetTextExtent(ListCtrl.Parent.hDC, sColString, Len(sColString)) Mod 65536
  251.  
  252.       ' Debug.Print ">"; sColString; "< W:"; nDataWidth
  253.  
  254.       'Ignore data columns for which there is no heading.
  255.  
  256.       If nColCount <= nNbrListboxCols Then
  257.         'If any data value is wider than the current column width,
  258.         'it becomes the new column width.
  259.  
  260.         If nDataWidth > nColWidth(nColCount) Then
  261.         nColWidth(nColCount) = nDataWidth
  262.         End If
  263.       End If
  264.  
  265.       If nTabPos > 0 Then
  266.         nColCount = nColCount + 1
  267.  
  268.       '  If nTabPos < Len(sColData) Then
  269.       '     nInStart = nTabPos + 1
  270.       '  End If
  271.       End If
  272.       nInStart = nTabPos + 1
  273.       Loop Until nTabPos = 0
  274.     End If
  275.   
  276. Next
  277.  
  278. Return
  279.  
  280. '==========================
  281. UpdateCtrls:
  282. '==========================
  283. 'Set the textbox font characteristics equal
  284. 'to the listbox font characteristics.
  285.  
  286. TextCtrl.Enabled = False
  287. TextCtrl.FontName = ListCtrl.FontName
  288. TextCtrl.FontSize = ListCtrl.FontSize
  289. TextCtrl.FontBold = ListCtrl.FontBold
  290. TextCtrl.FontItalic = ListCtrl.FontItalic
  291. TextCtrl.Move ListCtrl.Left, ListCtrl.Top - TextCtrl.Height, ListCtrl.Width, TextCtrl.Height
  292.  
  293. ReDim nTabstop(nNbrTabstops)
  294.  
  295. 'Calculate tabstop values for each column, in "dialog units"
  296.  
  297. If nNbrTabstops > 0 Then
  298.    'Get the average character widths, in pixels, of the
  299.    'listbox font and the system font.
  300.  
  301.    nListFontAvgWidth = (nlWinAPI_GetTextExtent(ListCtrl.Parent.hDC, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52) Mod 65536) / 52
  302.    nSystemFontAvgWidth = nlWinAPI_GetDialogBaseUnits() Mod 65536
  303.  
  304.    'A "dialog unit" is defined as 1/4 of the average
  305.    'character width of the system font, in pixels.
  306.    'We've already measured the width of each column,
  307.    'in pixels, but it's not accurate enough to simply
  308.    'divide one value into the other.
  309.  
  310.    'Note that errors in precision will start to creep in
  311.    'at this point, due to integer rounding and intermediate
  312.    'calculation results.  Experience shows that a little
  313.    'extra white space between the data columns helps to
  314.    'compensate (see "nSpaceBetweenCols").
  315.  
  316.    'Since a dialog unit is based on the system font,
  317.    'not the font we're actually using in the listbox,
  318.    'we must factor in the difference between the two
  319.    'average character widths.  Thus, a more accurate
  320.    'divisor is calculated as follows.
  321.  
  322.    fFontRatio = nListFontAvgWidth / nSystemFontAvgWidth
  323.    fListFontPixelsPerDlgUnit = (nSystemFontAvgWidth * fFontRatio) / 4
  324.  
  325.    'Set a tabstop at the dialog unit closest to the
  326.    'right-hand boundary (width) of each column.
  327.  
  328.    nTabstop(0) = nColWidth(1) / fListFontPixelsPerDlgUnit
  329.    For nTabSub = 2 To nNbrTabstops
  330.       nTabstop(nTabSub - 1) = nTabstop(nTabSub - 2) + nColWidth(nTabSub) / fListFontPixelsPerDlgUnit
  331.    Next
  332. Else
  333.    nTabstop(0) = 0
  334. End If
  335.  
  336. 'Activate the tabstops.
  337.  
  338.  
  339. nlRC = nlWinSetTabStops(TextCtrl.hWnd, nEM_SETTABSTOPS, nNbrTabstops, nTabstop(0))
  340. nlRC = nlWinSetTabStops(ListCtrl.hWnd, nLB_SETTABSTOPS, nNbrTabstops, nTabstop(0))
  341.  
  342. 'Redraw the controls.
  343.  
  344. TextCtrl.Refresh
  345. ListCtrl.Refresh
  346.  
  347. Return
  348.  
  349. End Function
  350.  
  351.